package com.lijie.ljblog.chess;

import com.lijie.ljblog.enums.ChessColorEnum;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static java.lang.Math.max;

/**
 * @Description
 * @Author lijie
 * @Date 2022/8/26 11:49
 **/
@Data
public class Chess {
    /**
     * 米字型搜索
     * [—, | , / , \]四个移动方向
     */
    static int[] inX = {1, 0, 1, 1};
    static int[] inY = {0, 1, 1, -1};

    /**
     * 评分表
     */
    static int[][] scoreTable = {
            //防守2子
            {0, 0, 0, 0, 0, 10000},
            //防守1子
            {0, 0, 20, 100, 500, 10000},
            //防守0子
            {0, 20, 100, 500, 2500, 10000}
    };
    /**
     * 棋盘大小
     */
    private int size;
    /**
     * 棋盘
     */
    private int[][] chessBoard;

    /**
     * 初始化一个棋盘
     *
     * @param size
     * @return
     */
    public static Chess initChess(int size) {
        Chess chess = new Chess();
        chess.setSize(size);
        chess.setChessBoard(new int[size][size]);
        return chess;
    }

    /**
     * 简单电脑，遍历棋盘，返回权重值最大的 棋点
     *
     * @param color 颜色
     * @return
     */
    public ChessPoint AiChesser(int color) {
        int max = Integer.MIN_VALUE;
        //最佳点位,以防有多个相同的最大值
        List<ChessPoint> chessPoints = new ArrayList<>();

        for (int i = 0; i < chessBoard.length; ++i) {
            for (int j = 0; j < chessBoard[i].length; ++j) {
                //判断是否为空位
                if (chessBoard[i][j] == 0) {
                    //进攻还是防守，寻找利益最大值
                    int score = max(getScore(i, j, color), getScore(i, j, -color));
                    if (score >= max) {
                        if (score > max) {
                            chessPoints.clear();
                            max = score;
                        }
                        chessPoints.add(ChessPoint.init(i, j));
                    }
                }
            }
        }

        //在最大值中任取一个返回,大部分情况只有一个
        int r = new Random().nextInt(chessPoints.size());
        return chessPoints.get(r);
    }

    /**
     * 简单电脑
     * 当前棋盘下，获取 坐标（x,y）的权重得分
     *
     * @param x
     * @param y
     * @param color
     * @return
     */
    private int getScore(int x, int y, int color) {
        //返回评分值
        int re = 0;

        //向 [—,|, /, \]四个方向搜索，对应inX，inY
        for (int i = 0; i < 4; ++i) {
            //k记录连子两侧空位的数目（非墙，非敌方落子）
            int k = 0;
            //记录连子的数目，初始值为1因为假设在当前位置落子
            int count = 1;

            //[—,|, /, \]四个方向的正负方向
            for (int j = -1; j < 2; j += 2) {
                int dx = x + j * inX[i];
                int dy = y + j * inY[i];

                //判断是否超出棋盘边界
                while (dx >= 0 && dx < chessBoard.length &&
                        dy >= 0 && dy < chessBoard[0].length) {
                    //假如遇到颜色相同的子，count+1，反之则退出循环，并判断此时有无空位
                    if (color * chessBoard[dx][dy] > 0) {
                        ++count;
                    } else {
                        if (color * chessBoard[dx][dy] == 0) {
                            ++k;
                        }
                        break;
                    }

                    dx += j * inX[i];
                    dy += j * inY[i];
                }
            }

            //假如连子大于5，使之等于5
            if (count > 5) {
                count = 5;
            }

            //加上该方向所得评分
            re += scoreTable[k][count];
        }
        return re;
    }

    /**
     * 检查游戏是否结束
     *
     * @param chess 黑子，白字枚举类
     * @param row   落子后棋盘的行
     * @param col   落子后棋盘的列
     * @return
     */
    public boolean checkWinner(ChessColorEnum chess, int row, int col) {
        // 以 row, col 为中心
        for (int c = col - 4; c <= col; c++) {
            // 针对其中的一种情况, 来判定这五个子是不是连在一起了~
            // 不光是这五个子得连着, 而且还得和玩家落的子是一样~~ (才算是获胜)
            try {
                if (chessBoard[row][c] == chess.getVal()
                        && chessBoard[row][c + 1] == chess.getVal()
                        && chessBoard[row][c + 2] == chess.getVal()
                        && chessBoard[row][c + 3] == chess.getVal()
                        && chessBoard[row][c + 4] == chess.getVal()) {
                    // 构成了五子连珠! 胜负已分!
                    return true;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                // 如果出现数组下标越界的情况, 就在这里直接忽略这个异，继续循环下一组数据
                continue;
            }
        }

        // 2. 检查所有列
        for (int r = row - 4; r <= row; r++) {
            try {
                if (chessBoard[r][col] == chess.getVal()
                        && chessBoard[r + 1][col] == chess.getVal()
                        && chessBoard[r + 2][col] == chess.getVal()
                        && chessBoard[r + 3][col] == chess.getVal()
                        && chessBoard[r + 4][col] == chess.getVal()) {
                    return true;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        // 3. 检查左对角线
        for (int r = row - 4, c = col - 4; r <= row && c <= col; r++, c++) {
            try {
                if (chessBoard[r][c] == chess.getVal()
                        && chessBoard[r + 1][c + 1] == chess.getVal()
                        && chessBoard[r + 2][c + 2] == chess.getVal()
                        && chessBoard[r + 3][c + 3] == chess.getVal()
                        && chessBoard[r + 4][c + 4] == chess.getVal()) {
                    return true;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        // 4. 检查右对角线
        for (int r = row - 4, c = col + 4; r <= row && c >= col; r++, c--) {
            try {
                if (chessBoard[r][c] == chess.getVal()
                        && chessBoard[r + 1][c - 1] == chess.getVal()
                        && chessBoard[r + 2][c - 2] == chess.getVal()
                        && chessBoard[r + 3][c - 3] == chess.getVal()
                        && chessBoard[r + 4][c - 4] == chess.getVal()) {
                    return true;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        // 胜负未分, 就直接返回 0 了.
        return false;
    }


    /**
     * 落子
     *
     * @param chess 黑子，白字枚举类
     * @param row   落子后棋盘的行
     * @param col   落子后棋盘的列
     * @return
     */
    public void putChess(ChessColorEnum chess, int row, int col) {
        if(chess!=null){
            chessBoard[row][col] = chess.getVal();
        }
    }

}
